#include "cobit.h"
#include "plcan.h"
#include "plcan_config.h"
#include "comath.h"
#include "costr.h"

/*TODO,jzy,delete NM*/
//#include "CanNm_Cfg.h"
//#include "CanNm.h"
//#include "plisotp.h"
//#include "pldesc.h"

#define INVALID_CONFIG_INDEX (0xFFU)
#define plcan_is_rx_canid_registered(canid)  (plcan_find_config_index_by_canid(canid) != INVALID_CONFIG_INDEX)

uint16 plcan_tx_busy_count = 0U;
uint16 plcan_tx_time_ms_count = 0U;
uint16 plcan_tx_period_common_multiple;

bool   plcan_is_busoff = false;
static bool   plcan_should_indicate_busoff_dtc = FALSE;
bool   plcan_should_record_busoff_dtc = FALSE;
static uint8  plcan_support_record_busoff_dtc_timecnt = 0;

uint16 plcan_elapsed_time_since_last_busoff_revocery = 0U;
uint16 plcan_elapsed_time_since_last_diag_request = 0xFFFFU;

uint8  plcan_busoff_recovery_thresholdcnt = 0U;
uint16 plcan_busoff_recovery_timecnt = 0U;
bool plcan_enable_transmit_msg = TRUE;
bool plcannm_disable_transmit = FALSE;
bool plcannm_disbale_receive  = TRUE;

/*Whether there has 29-bit extension CANID on the reception configuration*/
bool plcan_has_rx_ext_canid;

static void   plcan_msg_config_init(void);

void plcan_init_can(void) {
	plcan_tx_period_common_multiple = wordop_calc_period_lcm(plcan_tx_defs, plcan_tx_canid_count, PlcanTxidDef, transmit_period_ms);
	plcan_msg_config_init();
    
#if CANNM_DEFAULT_ENABLED == 1
	CanNm_Init();
	plcannm_disable_transmit = TRUE;
	plcannm_disbale_receive = TRUE;
#endif
    
}

static void plcan_msg_config_init(void) {
    uint8 index;

    memory_clear((uint8*)plcan_rx_infos, sizeof(plcan_rx_infos[0]) * plcan_rx_canid_count);
    memory_clear((uint8*)plcan_tx_infos, sizeof(plcan_tx_infos[0]) * plcan_tx_canid_count);
    
    for(index = 0U; index < plcan_rx_canid_count; index++) {
        if(plcan_rx_defs[index].config_index_ref != NULL) {
            (*(plcan_rx_defs[index].config_index_ref)) = index;
        }else{
            ;;
        }

        plcan_rx_infos[index].enabled = (plcan_rx_defs[index].canid!= 0U);
        plcan_rx_infos[index].config_index = index;
    }

    for(index = 0U; index < plcan_tx_canid_count; index++) {
        if(plcan_tx_defs[index].config_index_ref != NULL) {
            (*(plcan_tx_defs[index].config_index_ref)) = index;
        }else{
            
        }

        plcan_tx_infos[index].enabled  = (plcan_tx_defs[index].canid!= 0U);
        plcan_tx_infos[index].config_index = index;
        plcan_tx_infos[index].buffer.dlc = (uint8)(plcan_tx_defs[index].dlc); /*init the dlc from configuration table*/

        plcan_tx_infos[index].buffer_queued = FALSE;
        plcan_tx_infos[index].interrupt_suppressed = FALSE;
    }
}


void plcan_enter_sleep(void) {
	uint8 index;
	for(index = 0U; index < plcan_tx_canid_count; index++) {
		plcan_cancel_transmit(index);
	}
}

void plcan_error_handler(void) {

	uint8 index;
		plcan_is_busoff = TRUE;
		/* Perform handling only if no BusOff recovery is ongoing */
		if(plcan_busoff_recovery_timecnt == 0) {
			/* Switch the transmit path off */
			plcan_enable_transmit_msg = FALSE;

			/* and clear the transmit queue */

			for(index = 0U; index < plcan_tx_canid_count; index++) {
				plcan_cancel_transmit(index);
			}
			/* Start of BusOff recovery algorithm */
			if( plcan_busoff_recovery_thresholdcnt < cBusOffThresholdCL1ToL2 ) {
				plcan_busoff_recovery_thresholdcnt++;
				/* Fast BusOff recovery */
				plcan_busoff_recovery_timecnt = tBusOffRecoveryTimeL1;
			} else {
			/* Slow BusOff recovery */
				if(plcan_busoff_recovery_thresholdcnt < 0xFF){
					plcan_busoff_recovery_thresholdcnt++;
				}
				plcan_busoff_recovery_timecnt = tBusOffRecoveryTimeL2;
			}
		}else {
			; /* A BusOff must not occur during the BusOff recovery time, since the CAN-Driver is offline */
		}
}


void plcan_tx_busy_handler(void) {
	if(plcan_tx_busy_count < 0xFFFFU) {
		plcan_tx_busy_count++;
	}
}




void plcan_rxmsg_task(void) { /*TODO: when busoff*/
	uint8 index;
	const PlcanRxidDef *rxdef;
	PlcanRxInfo *rxinfo;
	bool rx_data_result;

	uint8 cb_rtn_value=VALUE_zero_no_error;

	for(index = 0U; index < plcan_rx_canid_count; index++) {
		rxdef = &(plcan_rx_defs[index]);
		rxinfo = &(plcan_rx_infos[index]);
		if(!(rxinfo->enabled)) {
			continue ;
		}

		if(IS_BUFF_EMPTY(rxinfo)){
			if((rxinfo->rx_timedout == FALSE) && (rxdef->timeout_cycle > 0U)) {
				rxinfo->nomsg_cycle_count += PLCFG_CAN_TRX_TASK_TIME;
				if(rxinfo->nomsg_cycle_count > rxdef->timeout_cycle) {
					rxinfo->rx_timedout = TRUE;
					rxinfo->rx_data_valid = FALSE;
					rxinfo->rx_data_invalid = TRUE;
					if((rxdef->timeout_callback) != NULL) {
						(rxdef->timeout_callback)(rxinfo->config_index);
					}
				}
			}
		} else {
            
            //Todo lock cpu
			//  drsys_lock_cpu();
		      while(!IS_BUFF_EMPTY(rxinfo)){

                  cb_rtn_value=VALUE_zero_no_error;

                  if((rxdef->pre_checker) != NULL) {
                      rx_data_result = (rxdef->pre_checker)(&(rxinfo->buffer[rxinfo->front]));
                  } else {
                      rx_data_result = TRUE;
                  }
                  if(rx_data_result && ((rxdef->pre_handler) != NULL)) {
                      /*pre handler is invoked only pre check is OK*/
                      rx_data_result = (rxdef->pre_handler)(&(rxinfo->buffer[rxinfo->front]), rxdef->pre_handler_arguments);
                  }
                  if(rx_data_result) {
                      rxinfo->rx_consecutive_data_error_count = 0U;
                  } else {
                      if(rxinfo->rx_consecutive_data_error_count < 0xFFFFU) {
                          rxinfo->rx_consecutive_data_error_count++;
                      }
                  }
                  rxinfo->rx_alive_count++;
                  if(rxinfo->rx_alive_count == 0U) {
                      rxinfo->rx_alive_count = 1U; /*after the first receiving, the alive count must always be greater than 0*/
                  }

                  if((rxdef->rx_callback) != NULL) {
                      cb_rtn_value=(rxdef->rx_callback)(&(rxinfo->buffer[rxinfo->front]), rx_data_result);
                  }

                  rxinfo->nomsg_cycle_count = 0x000U;
                  rxinfo->rx_timedout = FALSE;

                  rxinfo->rx_data_valid = rx_data_result;
                  rxinfo->rx_data_invalid = !rx_data_result;

                  /* Specified for UDS ISOTP layer {{{ */
                  if((cb_rtn_value==VALUE_buffer_locked)){
                      /* indication from DL to NW failed, buffer locked by APP */
                      break;
                  }else if((cb_rtn_value==VALUE_isotp_go_busy)){
                      /* break; and DEL */
                      BUFF_DEL(rxinfo);
                      break;
                  }else if(cb_rtn_value==VALUE_isotp_keep_busy){
                      /* just break; no DEL */
                      break;
                  }else if((cb_rtn_value==VALUE_isotp_leave_busy)){
                      /* No break; no DEL */
                  }else{
                      /* cb_rtn_value == 0; normal, no nw/app busy
                       * no break; but DEL
                       * */
                      BUFF_DEL(rxinfo);
                  }
                  /* Specified for UDS ISOTP layer }}} */
		      }
		//    if(os_running){Cpu_unlock();}else{}
		//	  drsys_unlock_cpu();
		}
	}
}


void plcan_receive_handler(uint32 can_id,uint8 dlc,const uint8 *buf) {
    
    uint8 index;
	uint8 config_index = INVALID_CONFIG_INDEX;
	PlcanRxInfo *rxinfo;

	for(index = 0U; index < plcan_rx_canid_count; index++) {
		if(can_id == plcan_rx_defs[index].canid){
			config_index = index;
			break;
		}
	}
    
    /*Only registered canid can be handled*/
    if((config_index == plcan_rx_canid_count) || (config_index == INVALID_CONFIG_INDEX)){
        return;
    }
    
        //Todo NM
//	if(is_nw_pdu_range(can_id)) {
//		config_index = plcannm_rx_config_index;
//	}

    
     if(plcan_rx_infos[config_index].enabled) { 
        rxinfo =  &(plcan_rx_infos[config_index]);
        if(IS_BUFF_FULL(rxinfo)){
            //Export Error flag here, !!!TODO::!!!
            BUFF_OVERADD(rxinfo,buf,dlc);
        }else{
            BUFF_ADD(rxinfo,buf,dlc);
        }
    }
}


void plcan_txmsg_task(void)  {
	uint8 index;
	const PlcanTxidDef *txdef;
	PlcanTxInfo *txinfo;
	for(index = 0U; index < plcan_tx_canid_count; index++) {
		 txdef = &(plcan_tx_defs[index]);
		 txinfo = &(plcan_tx_infos[index]);
		 if((!txinfo->enabled) || (txdef->transmit_period_ms == 0U)) {
			if(txinfo->buffer_queued) {
				plcan_transmit_byconfig(index, FALSE); /*retransmit*/
			}else{

			}
			continue ;
	     }
		if((plcan_enable_transmit_msg == TRUE) && (plcannm_disable_transmit != TRUE)){
		    if((plcan_tx_time_ms_count % (txdef->transmit_period_ms)) == (txdef->transmit_offset_ms)) {
			plcan_transmit_byconfig(index, TRUE);
		    } else {
			   if(txinfo->buffer_queued) {
				plcan_transmit_byconfig(index, FALSE); /*retransmit*/
			    }
		     }
	     }else{
		 }
	}
	if((plcan_enable_transmit_msg == TRUE) && (plcannm_disable_transmit != TRUE)){
		plcan_tx_time_ms_count += PLCFG_CAN_TRX_TASK_TIME;
		if(plcan_tx_time_ms_count >= plcan_tx_period_common_multiple) {
			plcan_tx_time_ms_count = 0U;
		}
	}else{
		plcan_tx_time_ms_count = 0U;
	}

	plcan_busoff_diag_task();
}

void plcan_transmit_complete_handler(uint32 can_transmit_last_id) {

	uint8 current_tx_index = 0;
	const PlcanTxidDef *txdef;
    PlcanTxInfo *txinfo;

	/*TODO,jzy, check hth action*/
	for (current_tx_index = 0; current_tx_index < plcan_tx_canid_count;current_tx_index++) {
		txdef = &(plcan_tx_defs[current_tx_index]);
		if (txdef->canid == can_transmit_last_id) {
			txinfo = &(plcan_tx_infos[current_tx_index]);

			if (txinfo->interrupt_suppressed) {
				/* Current HTH TX-Complete callback is suppressed, maybe caused by TX-Abort */
			} else {
				if ((txdef->tx_callback) != NULL) {
					(txdef->tx_callback)(current_tx_index, TRUE);
				} else {
				}
				if (plcan_enable_transmit_msg) {
					plcan_busoff_recovery_thresholdcnt = 0;
					plcan_busoff_recovery_timecnt = 0;
					plcan_is_busoff = FALSE;
				}

				//            if(plcan_busoff_recovered) { /*bus is chaned to on only if bus is recovered and at least on frame has been sent successfully*/
				//                plcan_buson_hold_time = 0x00U;
				//                plcan_tx_success_count_since_last_buson = 0x00U;

				//                plcan_busoff_recovered = false;
				//                plcan_is_busoff = false;
				//            }else{
				//            }

				//            if((!plcan_is_busoff) && (plcan_tx_success_count_since_last_buson < 0xFFFFU)) {
				//                plcan_tx_success_count_since_last_buson++;
				//            }else{
				//           }
			}
		} else {
		}
	}



}

void plcan_transmit_byconfig(uint8 config_index, bool should_call_fill) {
	const PlcanTxidDef *txdef;
	PlcanTxInfo *txinfo;
	bool fill_result = TRUE;
	uint8 transmit_result = 0x01;

    
    uint32 can_id = 0x00U;
	//Can_PduType tmp_pdu;
	if((plcan_enable_transmit_msg == TRUE) && (plcannm_disable_transmit!= TRUE)){
		if(config_index < plcan_tx_canid_count) {
			txdef = &(plcan_tx_defs[config_index]);
			txinfo = &(plcan_tx_infos[config_index]);
			if(txinfo->enabled) {
				if((txdef->fill_handler) != NULL && should_call_fill) {
					fill_result = (txdef->fill_handler)(&(txinfo->buffer), txdef->fill_handler_arguments);
				}else{
				}

				if(fill_result) { /*Transmit only data is filled correctly*/

					/* Enable TX-Complete callback function */
					txinfo->interrupt_suppressed = FALSE;

					if(txdef->post_filler != NULL && should_call_fill) {
						(txdef->post_filler)(&(txinfo->buffer));
					}else{
					}

//					tmp_pdu.swPduHandle     = 0;        /* Dummy currently */
//					tmp_pdu.length          = txdef->dlc;
//					tmp_pdu.id              = txdef->canid;
//					tmp_pdu.sdu             = (uint8*)&(txinfo->buffer.data[0U]);

                    
//                    can_id = txdef->canid;
//                     
//                    CAN_Send(CAN_ID_0, 0x01U, 
//                        const CAN_MessageInfo_t *txInfo,
//                        uint32_t msgId, const uint8_t *msgData);
//                        
//					transmit_result = Can_Write(plcan_tx_defs[config_index].device_hwId, config_index, &tmp_pdu);

                    
                     
    
    
                    
                    //Todo CAN Tx
                    
					if(transmit_result == 0x01) {
						txinfo->buffer_queued = FALSE;
					} else {
						txinfo->buffer_queued = TRUE;
					}
				}else{
				}
			}else {
				/*181 Test Case:DS28Func_Function
				 * For NM Directly call function "plcan_transmit_byconfig"
				 * */

				if((txdef->tx_callback) != NULL) {
					(txdef->tx_callback)(config_index, TRUE);
				}
			}
		}else{
		}
	}
}

void plcan_cancel_transmit(uint8 config_index) {
    PlcanTxInfo *txinfo;
    txinfo = &(plcan_tx_infos[config_index]);
    txinfo->buffer_queued = FALSE;

    /* MCAL implements CAN transmission by queue, which makes direct abortion of queued message difficult,
     * here, just suppressed the TX-Complete callback function of queued message temporarily
     * */
    txinfo->interrupt_suppressed = TRUE;
}

void plcan_busoff_diag_task(void) {
    
	if(plcan_busoff_recovery_timecnt != 0) {
		plcan_busoff_recovery_timecnt -= PLCFG_CAN_TRX_TASK_TIME;
		if(plcan_busoff_recovery_timecnt == 0) {
            
			//Todo replace zx mcu can busoff Can_Busoff_Recovery();
	  		plcan_enable_transmit_msg = TRUE;
		}
	}

	if(plcan_busoff_recovery_thresholdcnt >= nBusOffCountRecordDTCThreshold) {
		plcan_should_indicate_busoff_dtc = TRUE;
	}

    /*record time 30ms occupy the tenth(cBusOffThresholdCL1ToL2) recovery time
     * set the record after the tenth recovery time
     *
     * */
	if((plcan_should_indicate_busoff_dtc == TRUE) && (plcan_busoff_recovery_thresholdcnt != nBusOffCountRecordDTCThreshold)) {
		plcan_should_record_busoff_dtc = TRUE;
	}

	if(plcan_should_record_busoff_dtc == TRUE) {
		plcan_support_record_busoff_dtc_timecnt += PLCFG_CAN_TRX_TASK_TIME;
		if(plcan_support_record_busoff_dtc_timecnt >= 40U) {
			plcan_support_record_busoff_dtc_timecnt = 40U;
			if((plcan_is_busoff == FALSE) || (plcan_busoff_recovery_thresholdcnt < cBusOffThresholdCL1ToL2)) {
				plcan_should_record_busoff_dtc = FALSE;
				plcan_should_indicate_busoff_dtc = FALSE;
				plcan_support_record_busoff_dtc_timecnt = 0U;
			}
		}
	}



	if(plcan_is_busoff) {
		plcan_elapsed_time_since_last_busoff_revocery = 0U;
	}else {
		if(plcan_elapsed_time_since_last_busoff_revocery <= (0xFFFFU - PLCFG_CAN_TRX_TASK_TIME)) {
			plcan_elapsed_time_since_last_busoff_revocery += PLCFG_CAN_TRX_TASK_TIME;
		}
	}
}


void plcan_enable_tx_by_group(uint8 can_group, bool enabled) {
	uint8 index;
	for(index = 0U; index < plcan_tx_canid_count; index++) {
		const PlcanTxidDef *txconfig = &(plcan_tx_defs[index]);
		PlcanTxInfo *txinfo = &(plcan_tx_infos[index]);
		if((txconfig->tx_can_group & can_group) != 0x00U) {
			txinfo->enabled = enabled;
		}
    }
}

void plcan_enable_rx_by_group(uint8 can_group, bool enabled) {
	uint8 index;
	for(index = 0U; index < plcan_rx_canid_count; index++) {
        
		const PlcanRxidDef *rxconfig = &(plcan_rx_defs[index]);
		PlcanRxInfo *rxinfo = &(plcan_rx_infos[index]);
		if((rxconfig->rx_can_group & can_group) != 0x00U) {
			rxinfo->enabled = enabled;
		}
	}
}

void plcan_trxmsg_task(void){
	plcan_rxmsg_task();
	//pldesc_task();
	//plisotp_task();
	plcan_txmsg_task();
}


